library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.STD_LOGIC_ARITH.all;

entity cache is
  port (
    clock                    : in  std_logic;
    we, oe                   : in  std_logic;
    --high                     : in  std_logic;
    address                  : in  std_logic_vector(9 downto 0);
    miss                     : out std_logic;
    addressMemory            : out std_logic_vector(9 downto 0);
    weMemory, oeMemory       : out std_logic;
    bidir                    : inout std_logic_vector(15 downto 0);
    bidirMemory              : inout std_logic_vector(15 downto 0);
    inst_data                : in std_logic   -- 1 instruction 0 data
  );
end entity cache;

architecture a of cache is

   type cache_type is array (3 downto 0) of std_logic_vector(31 downto 0);
   type line_address is array (3 downto 0) of std_logic_vector(8 downto 0);
   type dirty_bit is array (3 downto 0) of std_logic;
   type valid_bit is array (3 downto 0) of std_logic;
   type lru is array (3 downto 0) of integer range 0 to 3;
   signal cacheMemory                : cache_type;
   signal cacheLineAddress           : line_address;
   signal dirtyBit                   : dirty_bit:= "0000";
   signal validBit                   : valid_bit:= "0000";
   signal lruBits                    : lru:=(3,3,3,3);
   signal index                      : integer range 0 to 3;
begin
  
  CacheProc: process(clock) is
  variable tempMiss      : std_logic;
  begin
    if rising_edge(clock) then
        if tempMiss = '1' then         -- Delay 3ashan neread men el memory
            miss <= '0';
            tempMiss := '0';
            oeMemory <= oe;
            weMemory <= we;
            if inst_data = '0' then    -- Data is data
               if dirtyBit(3) = '1' THEN      --Needs copying to memory WRITEBACK
                  miss <= '1';               -- To wait another round
                  tempMiss := '1';
                  oeMemory <= '0';
                  weMemory <= '1';
                  if (address(0) = '0') THEN
                     bidirMemory <= cacheMemory(3)(15 downto 0);   --Dih lazem 3ala mareteen, marra high we marra low.
                  else
                     bidirMemory <= cacheMemory(3)(31 downto 16);
                  end if;
                  dirtyBit(3) <= '0';
               else                           --Doesn't need WRITEBACK
                  if oe = '1' THEN
                      cacheMemory(3) <= bidirMemory;
                      bidir <= bidirMemory;
                  else
                      bidirMemory <= bidir;
                  end if;
                  if lruBits(3) /= 0 then         --adjust lruBits's
                     if lruBits(0) < lruBits(3) then
                         lruBits(0) <= lruBits(0) + 1;
                     end if;
                     if lruBits(1) < lruBits(3) then
                         lruBits(1) <= lruBits(1) + 1;
                     end if;
                     if lruBits(2) < lruBits(3) then
                         lruBits(2) <= lruBits(2) + 1;
                     end if;
                     lruBits(3) <= 0;
                  end if;
               end if;
            else                       -- Data is instruction
            end if;
        elsif we = '1' or oe = '1' then   --law read aw write
           miss <= '0';
           tempMiss := '0';
           if (address(9 downto 1) = cacheLineAddress(0) and validBit(0) = '1') THEN   --la2et-ha fel 2oola
               if we = '1' THEN               --law write set el dirty bit we da55al 3al cache
                   dirtyBit(0) <= '1';
                   if address(0) = '0' THEN
                       cacheMemory(0)(15 downto 0) <= bidir;
                   else
                       cacheMemory(0)(31 downto 16) <= bidir;
                   end if;
               else            --law read talla3 men el cache
                   if address(0) = '0' THEN
                       bidir <= cacheMemory(0)(15 downto 0);
                   else
                       bidir <= cacheMemory(0)(31 downto 16);
                   end if;
               end if;
               if lruBits(0) /= 0 then         --adjust lru's
                  if lruBits(1) < lruBits(0) then
                      lruBits(1) <= lruBits(1) + 1;
                  end if;
                  if lruBits(2) < lruBits(0) then
                      lruBits(2) <= lruBits(2) + 1;
                  end if;
                  if lruBits(3) < lruBits(0) then
                      lruBits(3) <= lruBits(3) + 1;
                  end if;
                  lruBits(0) <= 0;
               end if;                  
           elsif (address(9 downto 1) = cacheLineAddress(1) and validBit(1) = '1') THEN   --la2et-ha fel tanya
               if we = '1' THEN
                   dirtyBit(1) <= '1';
                   if address(0) = '0' THEN
                       cacheMemory(1)(15 downto 0) <= bidir;
                   else
                       cacheMemory(1)(31 downto 16) <= bidir;
                   end if;
               else
                   if address(0) = '0' THEN
                       bidir <= cacheMemory(1)(15 downto 0);
                   else
                       bidir <= cacheMemory(1)(31 downto 16);
                   end if;
               end if;
               if lruBits(1) /= 0 then         --adjust lru's
                  if lruBits(0) < lruBits(1) then
                      lruBits(0) <= lruBits(0) + 1;
                  end if;
                  if lruBits(2) < lruBits(1) then
                      lruBits(2) <= lruBits(2) + 1;
                  end if;
                  if lruBits(3) < lruBits(1) then
                      lruBits(3) <= lruBits(3) + 1;
                  end if;
                  lruBits(1) <= 0;
               end if;    
           elsif (address(9 downto 1) = cacheLineAddress(2) and validBit(2) = '1') THEN   --la2et-ha fel talta
               if we = '1' THEN
                   dirtyBit(2) <= '1';
                   if address(0) = '0' THEN
                       cacheMemory(2)(15 downto 0) <= bidir;
                   else
                       cacheMemory(2)(31 downto 16) <= bidir;
                   end if;
               else
                   if address(0) = '0' THEN
                       bidir <= cacheMemory(2)(15 downto 0);
                   else
                       bidir <= cacheMemory(2)(31 downto 16);
                   end if;
               end if;
               if lruBits(2) /= 0 then         --adjust lru's
                  if lruBits(0) < lruBits(2) then
                      lruBits(0) <= lruBits(0) + 1;
                  end if;
                  if lruBits(1) < lruBits(2) then
                      lruBits(1) <= lruBits(1) + 1;
                  end if;
                  if lruBits(3) < lruBits(2) then
                      lruBits(3) <= lruBits(3) + 1;
                  end if;
                  lruBits(2) <= 0;
               end if; 
           elsif (address(9 downto 1) = cacheLineAddress(3) and validBit(3) = '1') THEN   --la2et-ha fel rab3a
               if we = '1' THEN
                   dirtyBit(3) <= '1';
                   if address(0) = '0' THEN
                       cacheMemory(3)(15 downto 0) <= bidir;
                   else
                       cacheMemory(3)(31 downto 16) <= bidir;
                   end if;
               else
                   if address(0) = '0' THEN
                       bidir <= cacheMemory(3)(15 downto 0);
                   else
                       bidir <= cacheMemory(3)(31 downto 16);
                   end if;
               end if;
               if lruBits(3) /= 0 then         --adjust lruBits's
                  if lruBits(0) < lruBits(3) then
                      lruBits(0) <= lruBits(0) + 1;
                  end if;
                  if lruBits(1) < lruBits(3) then
                      lruBits(1) <= lruBits(1) + 1;
                  end if;
                  if lruBits(2) < lruBits(3) then
                      lruBits(2) <= lruBits(2) + 1;
                  end if;
                  lruBits(3) <= 0;
               end if;
           else      --mala2et-hash, set el miss 3ashan ne3mel el delay, we 3addy el signals lel memory
              miss <= '1';
              tempMiss := '1';
          end if;
       else
          tempMiss := '0';
          oeMemory <= '0';
          weMemory <= '0';
          bidir <= "ZZZZZZZZZZZZZZZZ";
       end if;
    end if;
  end process cacheProc;

   addressMemory <= address;
   --weMemory <= we;
   --oeMemory <= oe;
end architecture a;